/*
 * ========THE SOLMIX PROJECT=====================================
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.gnu.org/licenses/ 
 * or see the FSF site: http://www.fsf.org. 
 */

package org.solmix.fmk.servlet;

import java.io.IOException;
import java.io.Writer;
import java.util.Enumeration;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.oro.text.perl.Perl5Util;
import org.solmix.SLXConstants;
import org.solmix.commons.collections.DataTypeMap;
import org.solmix.commons.logs.Logger;
import org.solmix.fmk.serialize.JSParserFactoryImpl;
import org.solmix.fmk.util.OSGIHelper;
import org.solmix.services.exception.SLXException;
import org.solmix.services.serialize.JSParser;
import org.solmix.services.serialize.JSParserFactory;
import org.solmix.services.types.Texception;
import org.solmix.services.types.Tmodule;

/**
 * @author solomon
 * @since 0.0.1
 * @version $ID$ 2010-12-31 solmix-ds
 */
public class RequestContext
{

   /**
    * @return the request
    */
   public SLXHttpServletRequest getRequest()
   {
      return request;
   }

   /**
    * @param request the request to set
    */
   public void setRequest( SLXHttpServletRequest request )
   {
      this.request = request;
   }

   /**
    * @return the response
    */
   public HttpServletResponse getResponse()
   {
      return response;
   }

   /**
    * @param response the response to set
    */
   public void setResponse( HttpServletResponse response )
   {
      this.response = response;
   }

   /**
    * @return the pathInfo
    */
   public String getPathInfo()
   {
      return pathInfo;
   }

   /**
    * @param pathInfo the pathInfo to set
    */
   public void setPathInfo( String pathInfo )
   {
      this.pathInfo = pathInfo;
   }

   /**
    * @return the clientContextHeaderSent
    */
   public boolean isClientContextHeaderSent()
   {
      return clientContextHeaderSent;
   }

   /**
    * @param clientContextHeaderSent the clientContextHeaderSent to set
    */
   public void setClientContextHeaderSent( boolean clientContextHeaderSent )
   {
      this.clientContextHeaderSent = clientContextHeaderSent;
   }

   /**
    * @return the cachingEnabled
    */
   public boolean isCachingEnabled()
   {
      return cachingEnabled;
   }

   /**
    * @param cachingEnabled the cachingEnabled to set
    */
   public void setCachingEnabled( boolean cachingEnabled )
   {
      this.cachingEnabled = cachingEnabled;
   }

   private static DataTypeMap globalConfig;

   private static Logger log = new Logger( RequestContext.class.getName() );

   public static Logger staticLog;

   public ServletContext servletContext;

   public SLXHttpServletRequest request;

   public HttpServletResponse response;

   public HttpSession session;

   public String contentType;

   public String requestPath;

   public String servletPath;

   public String pathInfo;

   protected Writer out;

   public boolean clientContextHeaderSent;

   public boolean cachingEnabled;

   private JSParser jsParser;

   public static RequestContext instance( ServletRequest request, ServletResponse response ) throws Exception
   {
      return instance( (ServletContext) null, ( ( request ) ), ( ( response ) ) );
   }

   public static RequestContext instance( Servlet servlet, HttpServletRequest request, HttpServletResponse response ) throws SLXException
   {
      return instance( servlet.getServletConfig().getServletContext(), request, response );
   }

   public static RequestContext instance( Servlet servlet, HttpServletRequest request, HttpServletResponse response, Writer out ) throws SLXException
   {
      RequestContext context = instance( servlet.getServletConfig().getServletContext(), request, response );
      context.setOut( out );
      return context;
   }

   /**
    * @param servletContext
    * @param request
    * @param response
    * @return
    * @throws SLXException
    */
   public static RequestContext instance( ServletContext servletContext, ServletRequest request, ServletResponse response ) throws SLXException
   {
      RequestContext context = new RequestContext();
      context.init( servletContext, (HttpServletRequest) request, (HttpServletResponse) response );
      return context;
   }

   public RequestContext()
   {
      out = null;
      clientContextHeaderSent = false;
      cachingEnabled = true;
      contentType = null;
   }

   protected static DataTypeMap globalConfig()
   {
      if ( globalConfig == null )
         globalConfig = OSGIHelper.getCM().getSubtree( "RequestContext" );
      return globalConfig;
   }

   public RequestContext( Servlet servlet, HttpServletRequest request, HttpServletResponse response ) throws SLXException
   {
      out = null;
      clientContextHeaderSent = false;
      cachingEnabled = true;
      contentType = null;
      init( servlet.getServletConfig().getServletContext(), request, response );
   }

   /**
    * Return the ServletResponse writer.if the content type is null set "text/html"
    * 
    * @return the out
    * @throws SLXSLXException
    */
   public Writer getOut() throws SLXException
   {
      if ( out == null )
      {
         if ( log.isDebugEnabled() )
         log.debug( "Getting output stream via servletResponse.getWriter()" );
         if ( contentType == null )
            setContentType( globalConfig.getString( "defaultMimeType", "text/html" ) );
         try
         {
            setOut( response.getWriter() );
         } catch ( IOException e )
         {
            throw new SLXException( Tmodule.SERVLET, Texception.IO_EXCEPTION, "ioexception with response.getWriter()", e );
         }
      }
      return out;
   }

   /**
    * @param out the out to set
    */
   public void setOut( Writer out )
   {
      this.out = out;
   }

   /**
    * @param request
    * @return
    */
   public static String getRequestPath( HttpServletRequest request )
   {
      String requestPath = request.getRequestURI();
      if ( requestPath.indexOf( "//" ) != -1 )
      {
         Perl5Util regex = new Perl5Util();
         requestPath = regex.substitute( "s#//#/#g", requestPath );
      }
      return requestPath;
   }

   /**
    * @param servletContext
    * @param request
    * @param response
    * @throws SLXException
    */
   public void init( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response ) throws SLXException
   {
      globalConfig();
      JSParserFactory jsFactory = new JSParserFactoryImpl();
      jsParser = jsFactory.get();
      this.servletContext = servletContext;
      if ( OSGIHelper.servletContext == null && !SLXConstants.isOSGI() )
         OSGIHelper.servletContext = servletContext;
      requestPath = getRequestPath( request );
      this.session = request.getSession();
      servletPath = request.getServletPath();
      pathInfo = request.getPathInfo();
      this.request = new SLXHttpServletRequest( request );
      this.response = response;
      if ( globalConfig.getBoolean( "reportParams", false ) )
      {
         if ( log.isDebugEnabled() )
         log.debug( ( new StringBuilder() ).append( "Request parameters: " ).append( getParamsAsString() ).toString() );
      }
      if ( log.isInfoEnabled() )
      {
         String cgetHeader = request.getHeader( "If-Modified-Since" );
         boolean isCGET = cgetHeader != null && !"".equals( cgetHeader );
         boolean alreadyLoggedURL = request.getAttribute( "isc_alreadyLoggedURL" ) != null;
         if ( ( !isCGET || log.isDebugEnabled() ) && !alreadyLoggedURL )
         {
            request.setAttribute( "isc_alreadyLoggedURL", new Object() );
            log.info( ( new StringBuilder() ).append( isCGET ? "CGET " : "" ).append( "URL: '" ).append( requestPath ).append( "'" ).append(
               ", User-Agent: '" ).append( request.getHeader( "User-Agent" ) ).append( "'" ).append( ": " ).append(
               ServletTools.getBrowserSummary( this ) ).toString() );
         }
      }
      if ( globalConfig.getBoolean( "logCookies", false ) )
         logCookies();
      if ( globalConfig.getBoolean( "logHeaders", false ) )
         logHeaders();
      if ( staticLog == null )
         staticLog = new Logger( RequestContext.class.getName() );
   }

   /**
    * @return the servletContext
    */
   public ServletContext getServletContext()
   {
      return servletContext;
   }

   /**
    * @param servletContext the servletContext to set
    */
   public void setServletContext( ServletContext servletContext )
   {
      this.servletContext = servletContext;
   }

   /**
    * @return the session
    */
   public HttpSession getSession()
   {
      return session;
   }

   /**
    * @param session the session to set
    */
   public void setSession( HttpSession session )
   {
      this.session = session;
   }

   /**
    * @return the contentType
    */
   public String getContentType()
   {
      return contentType;
   }

   /**
    * @param contentType the contentType to set
    */
   public void setContentType( String contentType )
   {
      this.contentType = contentType;
   }

   /**
    * @return the requestPath
    */
   public String getRequestPath()
   {
      return requestPath;
   }

   /**
    * @param requestPath the requestPath to set
    */
   public void setRequestPath( String requestPath )
   {
      this.requestPath = requestPath;
   }

   /**
    * @return the servletPath
    */
   public String getServletPath()
   {
      return servletPath;
   }

   /**
    * @param servletPath the servletPath to set
    */
   public void setServletPath( String servletPath )
   {
      this.servletPath = servletPath;
   }

   public void logCookies()
   {
      if ( log.isInfoEnabled() )
         log.info( getCookiesAsString() );
   }

   public void logHeaders()
   {
      if ( log.isInfoEnabled() )
         log.info( getHeadersAsString() );
   }

   /**
    * Parser Headers as a String pair .Like:
    * 
    * <pre>
    * headerName = value
    * </pre>
    * 
    * @return
    */
   public String getHeadersAsString()
   {
      String output = "Client HTTP Headers:";
      Enumeration< ? > headers = request.getHeaderNames();
      if ( headers == null )
         output = ( new StringBuilder() ).append( output ).append( " request.getHeaderNames() returned null" ).toString();
      else
         while ( headers.hasMoreElements() )
         {
            String headerName = (String) headers.nextElement();
            output = ( new StringBuilder() ).append( output ).append( "\n" ).append( headerName ).append( ": " ).append(
               request.getHeader( headerName ) ).toString();
         }
      return output;
   }

   /**
    * Parser Cookies as a String pair .Like:
    * 
    * <pre>
    * Name:'name1',Value='vlaue1', domain:'..' , path:'..' , maxAge:'..' , isSecure:'..'
    * </pre>
    * 
    * @return
    */
   public String getCookiesAsString()
   {
      String output = "Cookies:";
      Cookie cookies[] = request.getCookies();
      if ( cookies == null )
      {
         output = ( new StringBuilder() ).append( output ).append( " NONE" ).toString();
         return output;
      }
      for ( int ii = 0; ii < cookies.length; ii++ )
         output = ( new StringBuilder() ).append( output ).append( "\nName: '" ).append( cookies[ii].getName() ).append( "', value: '" ).append(
            cookies[ii].getValue() ).append( "', domain: '" ).append( cookies[ii].getDomain() ).append( "', path: '" ).append( cookies[ii].getPath() ).append(
            "', maxAge: '" ).append( cookies[ii].getMaxAge() ).append( "', isSecure: '" ).append( cookies[ii].getSecure() ).append( "'" ).toString();

      return output;
   }

   public String getParamsAsString() throws SLXException
   {
      return jsParser.toJavaScript(request.getParams());
   }

   public void setNoCacheHeaders() throws SLXException
   {
      if ( response.isCommitted() )
         throw new SLXException( Tmodule.SERVLET, Texception.SERVLET_REQ_ALREADY_COMMITED,
            "Response has already been committed, unable to setNoCacheHeaders()" );
      if ( cachingEnabled )
      {
         setNoCacheHeaders( response );
         cachingEnabled = false;
      }
   }

   public static void setNoCacheHeaders( HttpServletResponse response )
   {
      if ( log.isDebugEnabled() )
      log.debug( "Setting headers to disable caching" );
      response.setHeader( "Cache-Control", "no-cache" );
      response.setHeader( "Pragma", "no-cache" );
      response.setDateHeader( "Expires", System.currentTimeMillis() );
   }
}
